-- XXX: split a lot of this out!
-- XXX2: also uh make some globals for things you use often, like prefs.

-- Profile(pn)
-- This returns a profile, preferably a player one.
-- If there isn't one, we fall back on the machine profile.
local function Profile(pn)
	if PROFILEMAN:IsPersistentProfile(pn) then
		-- player profile
		return PROFILEMAN:GetProfile(pn);
	else
		-- machine profile
		return PROFILEMAN:GetMachineProfile();
	end;
end;

-- PlayerScore(pn)
-- This function gets the top score for a player.
-- The old code that used to be here was absolutely terrible.
local function PlayerScore(pn)
	local s = AutoText("frutiger")..{
		Name="Score"..pn;
		InitCommand=cmd(shadowlength,1;diffuse,PlayerColor(pn);player,pn;Stroke,PlayerMidColor(pn));
		OnCommand=cmd(linear,0.2;diffusealpha,1;queuecommand,"Set");
		OffCommand=cmd(linear,0.2;diffusealpha,0);
		SetCommand=function(self)
			local SongOrCourse, StepsOrTrail;

			local scorelist;
			local text = "";
			local profile;

			if GAMESTATE:IsCourseMode() then
				SongOrCourse = GAMESTATE:GetCurrentCourse();
				StepsOrTrail = GAMESTATE:GetCurrentTrail(pn);
			else
				SongOrCourse = GAMESTATE:GetCurrentSong();
				StepsOrTrail = GAMESTATE:GetCurrentSteps(pn);
			end;

			if SongOrCourse then
				if StepsOrTrail then
					profile = Profile(pn);
					scorelist = profile:GetHighScoreList(SongOrCourse,StepsOrTrail);
					assert(scorelist);
					local scores = scorelist:GetHighScores();
					local topscore = scores[1];
					if not topscore then
						text = string.format("%.2f%%", 0);
					else
						text = string.format("%.2f%%", topscore:GetPercentDP()*100.0);
					end;
				else
					text = string.format("%.2f%%", 0);
				end;
			else
				text = string.format("%.2f%%", 0);
			end;
			self:settext(text);
		end;
		-- song and course changes
		CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
		CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
		-- steps and trail changes
		CurrentStepsP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentStepsP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;

		CurrentTrailP1ChangedMessageCommand=function(self)
			if pn == PLAYER_1 then self:playcommand("Set"); end;
		end;
		CurrentTrailP2ChangedMessageCommand=function(self)
			if pn == PLAYER_2 then self:playcommand("Set"); end;
		end;
	};

	return s;
end;

local t = LoadFallbackB()

t[#t+1] = Def.ActorFrame{
	-- OptionIcons
	Def.ActorFrame{
		Name="OptionIcons";

--[[
		-- stepmania's ModIconRow
		Def.ModIconRow  {
			Name="ModIconRowP1";
			InitCommand=cmd(x,SCREEN_CENTER_X-156;y,SCREEN_CENTER_Y-180);
			BeginCommand=function(self)
				if GAMESTATE:IsHumanPlayer(PLAYER_1) then
					self:visible(true);
				else
					self:visible(false);
				end;
			end;
			OnCommand=cmd(Load,"ModIconRowSelectMusic",PLAYER_1;diffusealpha,0;linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;diffusealpha,0);
			PlayerOptionsChangedP1MessageCommand=cmd(set,PLAYER_1);
		};

		Def.ModIconRow  {
			Name="ModIconRowP2";
			InitCommand=cmd(x,SCREEN_CENTER_X-156;y,SCREEN_CENTER_Y+180);
			BeginCommand=function(self)
				if GAMESTATE:IsHumanPlayer(PLAYER_2) then
					self:visible(true);
				else
					self:visible(false);
				end;
			end;
			OnCommand=cmd(Load,"ModIconRowSelectMusic",PLAYER_2;diffusealpha,0;linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;diffusealpha,0);
			PlayerOptionsChangedP2MessageCommand=cmd(set,PLAYER_2);
		};
--]]

		-- return of dubaiOne's custom mod icons:
		Def.ActorFrame{
			Name="MoonIconsHolder";
			InitCommand=function(self)
				-- xxx: encapsulate this into a function
				if IsUsingWideScreen() then
					if round(GetScreenAspectRatio(),5) == AspectRatios["SixteenNine"] then
						self:x(SCREEN_CENTER_X*0.36);
					else
						self:x(SCREEN_CENTER_X*0.2875);
					end;
				else
					self:x(SCREEN_CENTER_X*0.145);
				end;
			end;

			LoadActor("moonIcons", PLAYER_1)..{
				InitCommand=cmd(y,SCREEN_CENTER_Y-180;player,PLAYER_1);
				OnCommand=cmd(diffusealpha,0;linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};

			LoadActor("moonIcons", PLAYER_2)..{
				InitCommand=cmd(y,SCREEN_CENTER_Y+180;player,PLAYER_2);
				OnCommand=cmd(diffusealpha,0;linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};
	};

	-- frame (this belongs in the underlay but it's easier to work with here
	-- since so many objects are placed in it.)

	Def.ActorFrame{
		Name="Frame";
		InitCommand=cmd(x,SCREEN_CENTER_X-156;y,SCREEN_CENTER_Y);

		LoadActor( HDActor("_under") )..{
			Name="Under";
			InitCommand=cmd(diffusealpha,0);
			OnCommand=cmd(linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;zoomy,0;diffusealpha,1);
		};

		-- banner section
		Def.ActorFrame{
			Name="BannerSection";
			InitCommand=cmd(y,-102);

			-- stage number
			AutoText("frutiger",true)..{
				InitCommand=cmd(x,-128;y,-54;halign,0;diffuse,color("0,0,0,1");shadowlength,0;zoom,0.5;NoStroke);
				-- hide in event mode
				BeginCommand=cmd(visible,not PREFSMAN:GetPreference("EventMode"););
				OnCommand=cmd(playcommand,"Set");
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				SetCommand=function(self)
					local curStage = GAMESTATE:GetCurrentStage()
					local curStageNum = GAMESTATE:GetCurrentStageIndex(); -- 0-based
					local maxStages = PREFSMAN:GetPreference("SongsPerPlay")
					local text = ""

					local song = GAMESTATE:GetCurrentSong()
					if song then
						local songStages = 1;
						if song:IsLong() then songStages = 2
						elseif song:IsMarathon() then songStages = 3
						end
						local totalStages = curStageNum+songStages

						if totalStages == maxStages then
							curStage = 'Stage_Final'
						end
						text = THEME:GetString("Stage",string.sub(curStage,7));
						self:settext( text );

						if curStage == 'Stage_Final' then
							self:diffuse( color("0.8,0,0,1") );
						else
							self:diffuse( color("0,0,0,1") );
						end;
					else
						self:settext(text)
					end
				end;
				CurrentSongChangedMessageCommand=cmd(playcommand,"Set";);
			};

			-- sort message
			AutoText("frutiger",true)..{
				InitCommand=cmd(x,128;y,-54;halign,1;diffuse,color("0,0,0,1");shadowlength,0;zoom,0.5;NoStroke);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				UpdateCommand=function(self)
					-- change the text based on SortOrder
					local so = GAMESTATE:GetSortOrder();
					if so ~= nil then
						self:settext( SortOrderToLocalizedString(so) );
					end;
				end;
				SortOrderChangedMessageCommand=cmd(playcommand,'Update');
			};

			Def.ActorProxy {
				Name="BannerProxy";
				BeginCommand=function(self)
					local banner = SCREENMAN:GetTopScreen():GetChild('Banner');
					banner:scaletoclipped(256,80);
					self:SetTarget(banner);
				end;
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};

			-- tween time = either 0.2 or 0.65, depending on what you want.
			--[[
			Def.Quad{
				Name="FlashyJunk";
				InitCommand=cmd(zoomto,256,80;diffusealpha,0;blend,Blend.Add);
				ResetCommand=cmd(cropleft,0;cropright,0;fadeleft,0;faderight,0;diffusealpha,0.25);
				NextSongMessageCommand=cmd(stoptweening;playcommand,"Reset";linear,0.65;fadeleft,1;cropleft,1);
				PreviousSongMessageCommand=cmd(stoptweening;playcommand,"Reset";linear,0.65;faderight,1;cropright,1);
			};
			]]

			Def.ActorProxy{
				Name="CDTitleProxy";
				InitCommand=cmd(x,8;y,68);
				BeginCommand=function(self)
					-- load proxy
					local cdtitle = SCREENMAN:GetTopScreen():GetChild('CDTitle');
					if cdtitle then
						self:SetTarget(cdtitle[1]);
					end
				end;
			};

			LoadActor( THEME:GetPathB("","_frame 3x1"), "_bannerframe", 214 ) .. {
				InitCommand=cmd(diffusealpha,0);
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};

		-- bpm display
		Def.ActorFrame{
			Name="BPMSection";
			InitCommand=cmd(x,-96;y,-48;halign,0;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();

				if song or course then self:visible(true)
				else self:visible(false);
				end;
			end;

			Def.BPMDisplay{
				File=IsHighDefinition() and THEME:GetPathF("",fontAliases['frutiger'].Hires) or THEME:GetPathF("",fontAliases['frutiger'].Lores);
				Name="BPMDisplay";
				InitCommand=cmd(x,-4;halign,1;shadowlength,0;NoStroke);
				OnCommand=cmd(stoptweening);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
				SetCommand=function(self)
					-- stepmania 4 alpha 4 crashed on me once in course mode
					-- by messing up with the bpm select. I can't fix this theme-side;
					-- believe me, I tried:
					--[[
					local SongOrCourse;
					if GAMESTATE:IsCourseMode() then
						Trace("[bpmd] IT'S COURSE MODE!");
						 an assertion of
						-- pTrail failed, so...
						if GAMESTATE:GetCurrentTrail( GAMESTATE:GetMasterPlayerNumber() ) then
							Trace("[bpmd] trail!");
							self:SetFromGameState();
						else
							Trace("[bpmd] no trail");
							return;
						end;
					else
						self:SetFromGameState();
					end;
					]]
					self:SetFromGameState();
				end;
				CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
				CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			};

			AutoText("frutiger")..{
				Name="BPMLabel";
				InitCommand=cmd(halign,0;settext,"BPM";shadowlength,0;diffuse,color("#000000");NoStroke);
				OffCommand=cmd(linear,0.2;diffusealpha,0);
			};
		};

		-- number of stages in a course
		Def.ActorFrame{
			Name="CourseStagesSection";
			InitCommand=cmd(x,16;y,-48;zoom,0.75;visible,GAMESTATE:IsCourseMode());
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				local course = GAMESTATE:GetCurrentCourse();
				if course then self:visible(true)
				else self:visible(false);
				end;
			end;

			AutoText("frutiger") .. {
				Name="CourseSongs";
				InitCommand=cmd(shadowlength,0;diffusealpha,0;diffuse,color("#000000");NoStroke);
				OnCommand=cmd(linear,0.2;diffusealpha,1);
				OffCommand=cmd(linear,0.2;diffusealpha,0);

				CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");

				SetCommand=function(self)
					if not GAMESTATE:IsCourseMode() then return; end;

					local course = GAMESTATE:GetCurrentCourse();
					local stages;
					if course then
						stages = course:GetEstimatedNumStages();
						local append = course:GetEstimatedNumStages() ~= 1 and "Stages" or "Stage";
						self:settext( string.format(THEME:GetString("ScreenSelectCourse","Format"..append),stages) )
					else
						stages = "?";
						self:settext(stages)
					end;
				end;
			};
		};

		-- song length
		Def.ActorFrame{
			Name="LengthSection";
			InitCommand=cmd(x,108;y,-48;halign,1;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();
				
				if song or course then self:visible(true)
				else self:visible(false);
				end;
			end;

			AutoText("frutiger") .. {
				Name="SelectionLength";
				InitCommand=cmd(shadowlength,0;diffusealpha,0;diffuse,color("#000000");NoStroke);
				OnCommand=cmd(linear,0.2;diffusealpha,1;playcommand,"Set");
				OffCommand=cmd(linear,0.2;diffusealpha,0);

				CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
				CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");

				SetCommand=function(self)
					local song;
					local seltime;

					if GAMESTATE:IsCourseMode() then
						-- pulse takes care of this for us, somehow.
						if GAMESTATE:GetCurrentTrail(GAMESTATE:GetMasterPlayerNumber()) then
							local trail = GAMESTATE:GetCurrentTrail(GAMESTATE:GetMasterPlayerNumber());
							seltime = TrailUtil.GetTotalSeconds( trail );
						end;
					else
						song = GAMESTATE:GetCurrentSong();
						self:visible(song ~= nil)
						if song then
							seltime = song:MusicLengthSeconds();
						end
					end;

					if seltime then
						if seltime == 105.0 then
							-- r21 lol; it seems to work on other songs but
							-- if a song is exactly 1:45 it won't work lol
							self:settext("not 1:45");
						else
							local finalText = SecondsToMSSMsMs(seltime);
							self:settext( string.sub(finalText, 0, string.len(finalText)-3) );
						end;
					else
						self:settext( "xx:xx" );
					end;
				end;
			};
		};

		-- Autogen
		LoadActor("Autogen",PLAYER_1)..{
			InitCommand=cmd(x,-96;y,-24;halign,0;player,PLAYER_1);
		};
		LoadActor("Autogen",PLAYER_2)..{
			InitCommand=cmd(x,96;y,-24;halign,1;player,PLAYER_2);
		};

		-- fallback controller
		Def.Actor{
			InitCommand=function(self)
				if not GetUserPref("SSMusicInfo") then
					-- write fallback
					SetUserPref("SSMusicInfo","PaneDisplay");
				end;
			end;
		};

		-- DifficultyList
		--[[
		LoadActor("difficultylist")..{
			InitCommand=cmd(x,-80);
			BeginCommand=function(self)
				if GetUserPref("SSMusicInfo") == "DifficultyList" and not GAMESTATE:IsCourseMode() then self:visible(true);
				else self:visible(false);
				end;
			end;
		};
		]]

		-- RadarBars
		LoadActor("radarbars")..{ InitCommand=cmd(x,-80); };

		-- PaneDisplay
		LoadActor("panedisplay")..{ InitCommand=cmd(x,-80;y,4); };

		-- Course Contents
		LoadActor("coursecontents")..{ InitCommand=cmd(x,-80); };

		-- RadarBars & PaneDisplay requires difficulties.
		Def.ActorFrame{
			Name="Difficulties";
			InitCommand=cmd(y,128);
			BeginCommand=function(self)
				if GetUserPref("SSMusicInfo") == "RadarBars" or GetUserPref("SSMusicInfo") == "PaneDisplay" then
					self:visible(true);
				else
					self:visible(false);
				end;
			end;

			LoadActor("PlayerDifficulty", PLAYER_1)..{
				InitCommand=cmd(x,-136;halign,0);
			};

			LoadActor("PlayerDifficulty", PLAYER_2)..{
				InitCommand=cmd(x,136;halign,1);
			};
		};

		-- scores
		Def.ActorFrame{
			Name="ScoresSection";
			InitCommand=cmd(y,150;zoom,0.75);
			CurrentSongChangedMessageCommand=cmd(playcommand,"Set");
			CurrentCourseChangedMessageCommand=cmd(playcommand,"Set");
			SetCommand=function(self)
				-- check for a song or course. if there isn't one, hide it.
				local song = GAMESTATE:GetCurrentSong();
				local course = GAMESTATE:GetCurrentCourse();
				self:visible(song ~= nil or course ~= nil)
			end;

			PlayerScore(PLAYER_1)..{ InitCommand=cmd(x,-172;halign,0); };
			PlayerScore(PLAYER_2)..{ InitCommand=cmd(x,172;halign,1); };
		};

		-- song options
		AutoText("frutiger") .. {
			Name="SongOptions";
			InitCommand=cmd(y,150;playcommand,"Set";zoom,0.5;shadowlength,1;diffusealpha,0;NoStroke);
			OnCommand=cmd(linear,0.2;diffusealpha,1);
			OffCommand=cmd(linear,0.2;diffusealpha,0);
			ExtraStageCommand=cmd(diffuseshift;effectcolor1,color("1,1,1,1");effectcolor2,color("0.8,0.9,1,1");effectperiod,3.5);

			SetCommand=function(self)
				local sText = GAMESTATE:GetSongOptionsString()
				sText = string.gsub(sText, ", ", "\n")
				self:settext(sText)
				if GAMESTATE:IsAnExtraStage() then
					self:playcommand("ExtraStage")
				else
					self:stopeffect()
				end
			end;
			SongOptionsChangedMessageCommand=cmd(playcommand,"Set");
		};
	};

	-- Fade out here; this and the message for the options should appear
	-- over every single object in this screen. The fade is stolen from
	-- Screen cancel because I like how it worked.
	Def.Quad{
		InitCommand=cmd(diffuse,color("#000000");FullScreen;diffusealpha,0);
		OffCommand=cmd(cropbottom,1;fadebottom,.5;linear,0.3;cropbottom,-0.5;diffusealpha,1);
	};

	AutoText("frutiger")..{
		Text=THEME:GetString("ScreenSelectMusic","Press Start for Options");
		InitCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;vertalign,bottom;NoStroke);
		OnCommand=cmd(visible,false);
		ShowPressStartForOptionsCommand=cmd(visible,true;zoom,1.5;decelerate,1;zoom,1);
		ShowEnteringOptionsCommand=cmd(settext,THEME:GetString("ScreenSelectMusic","Entering Options...");hurrytweening,0.1;sleep,1;accelerate,0.1;zoomy,0);
	};
};

-- my decorations
--t[#t+1] = StandardDecorationFromFile("SegmentDisplay", "SegmentDisplay"); -- song

-- player segment displays (non-decorations)
t[#t+1] = Def.ActorFrame{
	InitCommand=cmd(x,SCREEN_CENTER_X-156;CenterY;);
	LoadActor("PlayerSegmentDisplay",PLAYER_1)..{
		InitCommand=cmd(x,-86;y,-197;halign,0;player,PLAYER_1);
	};
	LoadActor("PlayerSegmentDisplay",PLAYER_2)..{
		InitCommand=cmd(x,86;y,197;halign,1;player,PLAYER_2);
	};
};

return t;